<?php
/**
 * Holds class references and manages class dependencies.
 * @copyright Nicolas Schudel 2008
 * @copyright Based on some code from the book "PHP in Action" by Reiersøl et.al.
 * @copyright Based on some code found at: http://www.sitepoint.com/forums/showthread.php?p=2631135
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License
 */

interface IServiceLocatable {
	public static function registerFactory($factory);
	public static function registerInstance($instance);
	public static function getService($service);
}

class ServiceLocator implements IServiceLocatable{
	private static $factories = array();
	private static $instances = array();
	
	private static function createInstance($classname, $args = array()) {
		$classname = strtolower($classname);
		$reflected = new ReflectionClass($classname);
		$constructor = $reflected -> getConstructor();
		$parameters = $constructor -> getParameters();
		foreach($parameters as $parameter) $args = self::createInstance($parameter -> getName(), $args);
		$callback = array(new ReflectionClass($classname), 'newInstance'); 
		return call_user_func_array($callback, array($args));
	}
	
	public static function registerFactory($factory) {
		$factory = strtolower($factory);
		if(is_array(self::$factories) && !in_array($factory, self::$factories)) self::$factories[$factory] = $factory;
	}
	
	public static function registerInstance($instance) {
		$classname = strtolower(get_class($instance));
		
		if(is_object($instance)) {
			if(is_array(self::$instances) && !array_key_exists($classname, self::$instances)) self::$instances[$classname] = $instance;
		}
	}
	
	public static function getService($service) {
		$service = strtolower($service);
		if(array_key_exists($service, self::$instances)) return self::$instances[$service];
		
		foreach(self::$factories as $factory) {
			if(array_key_exists($factory, self::$instances)) {
				$factory = self::$instances[$factory];
				if($factory -> hasService($service)) {
					$instance = $factory -> createService($service);
					self::registerInstance($instance);
					return $instance;
				}
			} else {
				$instance = self::createInstance($factory);
				self::registerInstance($instance);
				if($instance -> hasService($service)) {
					self::$instances[$service] = $instance -> createService($service);
					return self::$instances[$service];
				} else if(strtolower(get_class($instance)) == $service) {
					return $instance;
				}
			}
		}
	}
}
?>